1 预备知识

1 数据操作

本节介绍在深度学习中的数组: 张量 (tensor). 它类似 numpyndarray, 但是支持 GPU 加速计算、自动微分.

1.1 入门

PyTorch 为例.

#导入PyTorch
import torch

x = torch.arange(12)
#tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])

#张量的尺寸
x.shape # torch.Size([12])

#张量中总元素的个数
x.numel() # 12

修改张量尺寸:

X = x.reshape(3, 4)
'''
tensor([[0, 1, 2, 3],
        [4, 5, 6, 7],
        [8, 9, 10, 11]])
'''
Y = x.reshape(3, 2, 2)
'''
tensor([[0, 1],
        [2, 3]],
        
       [[4, 5],
        [6, 7]],
        
       [[8, 9],
        [10, 11]])
'''

这里 reshape((3, 4)) 效果是等同的

如果只指定行或者列, 然后自动计算另一个维度, 可以直接填入-1:

x.reshape(-1, 4) #指定四列
x.reshape(3, -1) #指定三行

全零: 输入一个 tuple (同样等效于 torch.zeros(2, 3, 4))

torch.zeros((2, 3, 4))
'''
tensor([[[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]],

        [[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]]])
'''

类似地有 torch.ones((2, 3, 4))

逐元素从概率分布取值:

torch.randn(3, 4) #每个元素从N(0,1)正态分布取值

从 Python 列表转换:

torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])

1.2 运算符

对两个 tensor 对象 x, y, 可以用 x+y, x-y, x*y, x/y, x**y 生成新的 tensor, 其中的元素进行逐元素运算.

对每个元素添加指数:

torch.exp(x)

多个 tensor 的连结 (concatenate):

X = torch.arange(12).reshape(3, 4)
Y = torch.tensor([2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1])
torch.cat((X, Y), dim=0) # 按行拼接
torch.cat((X, Y), dim=1) # 按列拼接
'''
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [ 2.,  1.,  4.,  3.],
        [ 1.,  2.,  3.,  4.],
        [ 4.,  3.,  2.,  1.]])
tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
        [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
        [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]])
'''

构建 Bool 型张量: (需要尺寸一致)

X == Y
'''
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])

对所有元素求和: 得到单元素张量(并非 float!)

X.sum() #tensor(66.)

#可以求所有行的元素:
X.sum(axis=0)

#非降维求和: 求和后保持轴的数量不变
X.sum(axis=1, keepdims=True)

点积: 按照 xTy 进行运算

X = torch.ndarray([0, 1, 2, 3])
y = torch.ones(4)
torch.dot(x, y) # tensor(6.)
#这等价于torch.sum(x * y)

矩阵、向量:

torch.mv(A, y) #矩阵、向量
torch.mm(A, B) #矩阵、矩阵

范数:

torch.norm(u) #L2范数
torch.abs(u).sum() #L1范数

1.3 广播机制

广播机制用于自动为两个尺寸不同的张量填充尺寸, 以进行运算

a = torch.arange(3).reshape(3, 1)
b = torch.arange(2).reshape(1, 2)
a + b
#a变成: tensor([[0, 0], [1, 1], [2, 2]])
#b变成: tensor([[0, 1], [0, 1], [0, 1]])
#a+b: tensor([[0, 1], [1, 2], [2, 3]])

1.4 索引 切片

索引、切片和 Python 数组一致:

X[-1], X[1:3]

#修改指定元素
X[1, 2] = 9
X[0:2, :] = 12 #0、1两行的所有列的值变为12

1.5 内存

在 PyTorch 中, 如果我们执行 Y = Y + X, 则 Y 的内存发生了改变. 为了改变这一点, 需要原地操作: 使用 Y += X, 或者切片操作 Y[:] = X + Y.

1.6 转换为其他 Python 对象

A = X.numpy() #将torch.Tensor变为numpy.ndarray
B = torch.tensor(A) #将numpy.ndarray变为torch.Tensor

大小为一的张量转换为标量:

a = torch.tensor([3,5])
a.item(), float(a), int(a) #3.5, 3.5, 3

2 微积分

3 自动微分

3.1 一个简单的例子

PyTorch 可以自动计算梯度

x = torch.arange(4.0)
x.requires_grad_(True) #等价于x = torch.arange(4., requires_grad=True)
x.grad #当前的值是默认值None

y = 2 * torch.dot(x, x) #tensor(28., grad_fn=<MulBackward0>)
y.backward()
x.grad #此时y'=4x, 因此tensor([0., 4., 8., 12.])
x.grad == 4*x #tensor([True, True, True, True])

现在计算另一个函数

x.grad.zero_() #默认情况下梯度会累积, 因此需要清除
y = x.sum()
y.backward()
x.grad #tensor([1., 1., 1., 1.])

3.2 非标量变量的反向传播

y 不是标量, y 关于 x 的导数是一个矩阵. 但在实际中我们依然只计算训练样本中每个组成部分的损失函数的导数.

x.grad.zero_()
y = x * x
y.sum().backward() #y.backward(torch.ones(len(x)))
#(x_1^2+...+x_n^2)'=2(x_1+...+x_n)
x.grad #tensor([0., 2., 4., 6.])

3.3 分离计算

有时候我们希望将某些计算放在计算图之外. 例如 y=x2,z=ux. 但是我们希望将 y 看作一个常数, 只考虑 xy 被计算后发挥的作用.

为此, 分离 y 来返回新变量 u, 有相同的值, 但丢弃计算图中计算 y 的任何信息(梯度不会向后流经 ux). 因此反向计算中 z=ux, 把 u 当作常数, 而非 u=x3.

x.grad.zero_()
y = x * x
u = y.detach()
z = u * x

z.sum().backward()
x.grad == u #tensor([True, True, True, True])

x.grad.zero_()
y.sum().backward()
x.grad == 2 * x #tensor([True, True, True, True])

Python 控制流的梯度计算

def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c
    
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()

a.grad == d/a #tensor(True)

概率

from torch.distributions import multinomial

fair_probs = torch.ones([6]) / 6
multinomial.Multinomial(1, fair_probs).sample()
#每次会输出形如tensor([0., 0., 1., 0., 0., 0.])

multinomial.Multinomial(10, fair_probs).sample()
#tensor([5, 3, 2, 0, 0, 0])

向量化的采样远快于普通的 for 循环.

Pasted image 20250425224639.png

查阅文档

查询随机数生成模块的所有属性

print(dir(torch.distributions))

#['AbsTransform', 'AffineTransform', 'Bernoulli', 'Beta', 'Binomial', 'CatTransform', 'Categorical', 'Cauchy', 'Chi2', 'ComposeTransform', 'ContinuousBernoulli', 'CorrCholeskyTransform', 'CumulativeDistributionTransform', 'Dirichlet', 'Distribution', 'ExpTransform', 'Exponential', 'ExponentialFamily', 'FisherSnedecor', 'Gamma', 'Geometric', 'Gumbel', 'HalfCauchy', 'HalfNormal', 'Independent', 'IndependentTransform', 'Kumaraswamy', 'LKJCholesky', 'Laplace', 'LogNormal', 'LogisticNormal', 'LowRankMultivariateNormal', 'LowerCholeskyTransform', 'MixtureSameFamily', 'Multinomial', 'MultivariateNormal', 'NegativeBinomial', 'Normal', 'OneHotCategorical', 'OneHotCategoricalStraightThrough', 'Pareto', 'Poisson', 'PowerTransform', 'RelaxedBernoulli', 'RelaxedOneHotCategorical', 'ReshapeTransform', 'SigmoidTransform', 'SoftmaxTransform', 'SoftplusTransform', 'StackTransform', 'StickBreakingTransform', 'StudentT', 'TanhTransform', 'Transform', 'TransformedDistribution', 'Uniform', 'VonMises', 'Weibull', 'Wishart', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'bernoulli', 'beta', 'biject_to', 'binomial', 'categorical', 'cauchy', 'chi2', 'constraint_registry', 'constraints', 'continuous_bernoulli', 'dirichlet', 'distribution', 'exp_family', 'exponential', 'fishersnedecor', 'gamma', 'geometric', 'gumbel', 'half_cauchy', 'half_normal', 'identity_transform', 'independent', 'kl', 'kl_divergence', 'kumaraswamy', 'laplace', 'lkj_cholesky', 'log_normal', 'logistic_normal', 'lowrank_multivariate_normal', 'mixture_same_family', 'multinomial', 'multivariate_normal', 'negative_binomial', 'normal', 'one_hot_categorical', 'pareto', 'poisson', 'register_kl', 'relaxed_bernoulli', 'relaxed_categorical', 'studentT', 'transform_to', 'transformed_distribution', 'transforms', 'uniform', 'utils', 'von_mises', 'weibull', 'wishart']

使用 help:

help(torch.ones)